Ecran Tactile Delphi Multi Touch - John COLIBRI. |
- résumé : gestion Delphi des écrans tactiles multi-points: type d'écrans, test, messages souris, déplacement d'objets, message wm_touch, moteur d'inertie Windows
- mots clé : multi-touch, écran tactile, RegisterTouchWindow, wm_Touch, PhysicalToLogicalPoint, iInertiaProcessor, iManipulationProcessor
- logiciel utilisé : Windows XP personnel (pour compiler), Windows 7 (pour exécuter), Delphi Xe3
- matériel utilisé : Pentium 2.800 Mhz, 512 Meg de mémoire, 250 Giga disque dur, PackBell (Windows 7), écran iiYama t2250Mts
- champ d'application : Delphi 2010 et supérieurs
- niveau : développeur Delphi Windows
- plan :
1 - Ecrans tactiles Windows Multi Touch Pour pouvoir recevoir les messages souris Windows correspondant au toucher, il faut disposer d'un équipement capable de détecter ces touchers, et installer
les pilotes Windows correspondants. Parmi les équipement, mentionnons - les portables tel que le HP TouchSmart (non essayé)
- les PC-Ecrans en un seul appareil (Sony Vaio L - non essayé)
- les touch-pads comme Dell Inspiron Mini 10 (non essayé)
- les moniteurs séparés. Par exemple le iiYama t2250Mts (utilisé pour cet article)
2 - Ecran tactile iiYama Touch Screen
Pour faire nos essais tactiles, nous avons utilisé un écran iiYama ProLite T2250Mts. Sur Amazon.fr, il nous a coûté 240 Euros TTC, livré en 2 jours.
2.1 - Installation Pour l'installation - connecter la prise écran
- connecter AUSSI la prise USB
- connecter la prise secteur
2.2 - Vérifications Pour Windows 7, en cliquant sur "Ordinateur | Propriétés", dans la section
"Système" la présence de fonctionnalités tactiles est confirmée: Nous pouvons aussi vérifier que l'écran tactile est bien reconnu comme
moniteur: Si toutefois ces deux conditions ne sont pas remplies, il est possible de télécharger le pilote iiYama pour le T2250Mts depuis le site iiYama.
3 - Principe Touch Screen Une fois l'écran installé, nous pourrons utiliser l'écran avec le doigt de la
même façon que nous utilisons la souris: - toucher l'écran a le même effet qu'un clic
- toucher et traîner le doigt se comporte comme un tirer-glisser (drag and drop)
Nous pouvons le vérifier simplement en chargeant dans le bloc note (NOTEPAD.EXE) quelques lignes, et de sélectionner quelques lignes avec le doigt:
Et pour simuler le toucher avec deux doigts, nous pouvons charger MSPAINT.EXE, initialiser une nouvelle image ("Fichier | Nouveau"), puis tirer-glisser deux doigts sur l'écran:
3.1 - Type d'écran Il existe fondamentalement deux générations d'écrans tactiles - les écrans à toucher simple (single touch) pour lequel chaque toucher sera converti en message souris
- les écrans à toucher multiple (multi-touch) qui intercepteront de un à 10 actions de toucher. Dans le cas de notre iiYama, il est capable de détecter deux toucher.
Sur Window avant Windows 7, les toucher étaient traduits en message souris usuels. Pour pouvoir distinguer les messages MouseMove des messages souris, le type tShiftState a été redéfini comme suit:
TShiftState = Set Of (ssShift, ssAlt, ssCtrl, ssLeft, ssRight, ssMiddle, ssDouble,
ssTouch, ssPen, ssCommand |
Nous pouvons donc détecter si OnMouseMove, par, exemple, a été provoqué par la souris ou par le toucher.
Voici un premier exemple utilisant le toucher simple qui affiche simplement les messages souris
| créez une nouvelle applications Vcl | |
créez les messages souris et affichez les informations souris correspondants
Type t_shift_type= (ssShift, ssAlt, ssCtrl, //
ssLeft, ssRight, ssMiddle, ssDouble, ssTouch, ssPen, ssCommand);
t_shift_type_set= Set Of t_shift_type;
Function f_shift_type_name(p_shift_type: t_shift_type): String;
Begin
Result:= GetEnumName(TypeInfo(t_shift_type), Integer(p_shift_type));
End;
Function f_shift_state_set(p_shift_state_set: tShiftState): String;
Var l_shift_type: t_shift_type;
l_shift_type_set: t_shift_type_set; Begin
Result:= '';
Move(p_shift_state_set, l_shift_type_set, SizeOf(t_shift_type_set));
For l_shift_type:= ssShift To ssCommand Do
If l_shift_type In l_shift_type_set
Then Result:= Result+ f_shift_type_name(l_shift_type)+ ' ';
End; // f_shift_state_set
Procedure display(p_text: String); Begin
Form1.Memo1.Lines.Add(p_text);
End; Var g_event_count: Integer= 0;
Procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
Begin display(Format('%3d %-15s %4d %4d %s',
[g_event_count, 'MouseDown', X, Y, f_shift_state_set(Shift)]));
Inc(g_event_count); End;
Procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer); Begin
display(Format('%3d %-15s %4d %4d %s',
[g_event_count, 'MouseMove', X, Y, f_shift_state_set(Shift)]));
Inc(g_event_count); End;
Procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
Begin display(Format('%3d %-15s %4d %4d %s',
[g_event_count, 'MouseUp', X, Y, f_shift_state_set(Shift)]));
Inc(g_event_count); End;
Procedure TForm1.FormClick(Sender: TObject);
Begin display(Format('%3d %-15s ',
[g_event_count, 'Click'])); Inc(g_event_count);
End;
Procedure TForm1.FormDblClick(Sender: TObject);
Begin display(Format('%3d %-15s ',
[g_event_count, 'DblClick'])); Inc(g_event_count);
End; | | | compilez |
| voici un exemple d'exécution: |
Notez que
- comme tShiftState utilise une définition sans passer par la définition préalable de l'énuméré, nous avons créé une définition "parallèle" pour pouvoir afficher les énumérés (cf le code)
- une fois que nous avons touché, la position courante de la souris est celle du dernier toucher. Et même si nous ne touchons plus à l'écran ou la souris, nous continuons à recevoir des messages OnMouseMove. Pour arrêter ces
messages, il suffit de déplacer le point sensible de la souris (avec la souris ou avec le doigt) en dehors du composant qui gère OnMouseMove
Et si nous tapotons deux fois l'écran, voici le résultat
4 - Mise en oeuvre Multi Touch Delphi
Nous pouvons aussi provoquer un OnClic sur un bouton par tapotement: | créez une nouvelle applications Vcl |
| créez les messages souris et affichez les informations souris correspondants
Procedure TForm1.Button1Click(Sender: TObject); //
Begin display(Format('%3d %-15s ',
[g_event_count, 'BtnClick'])); Inc(g_event_count);
End; | | | compilez |
| voici un exemple d'exécution: |
Notez que
- lorsque nous touchons Button1, un message OnClic est bien généré
- toutefois le clic ne provoque pas l'effet visuel d'enfoncement, comme pour un clic souris.
- pour apercevoir l'effet d'enfoncement il faut déplacer le doigt lors du toucher.
4.1 - Toucher multiple (multi touch) Sur Windows 7, les touchers sont traduits en un message wm_Touch qui comporte
de nombreuses informations sur les touchers.
Pour utiliser wm_Touch, il faut - avoir un écran tactile
- utiliser Windows 7 ou supérieur
- enregistrer (et dé-enregistrer) chaque fenêtre censée recevoir wm_Touch
- créer un gestionnaire d'événements wm_Touch
4.1.1 - Enregistrement de la fenêtre
Cet enregistrement est effectué en appelant RegisterTouchWindow en fournissant la poignée de la fenêtre. Cet appel pourra donc se faire dans CreateWnd ou dans tForm.OnCreate. Par exemple :
Procedure TForm1.FormCreate(Sender: tObject);
Begin RegisterTouchWindow(Handle, 0);
End; // FormCreate |
Et lorsque nous n'avons plus besoin de recevoir les événements wm_Touch, nous
appellerons UnregisterTouchWindow(Handle). Par Exemple dans DestroyWnd ou dans OnDestroy
4.1.2 - Gestionnaire wm_Touch Nous créons donc un gestionnaire d'événements
Procedure WMTouch(Var Message: TMessage); message WM_TOUCH;
| et pour ce message: - Message.lParam est est la poignée pour les informations touch
- Message.wParam est le nombre de touchers (1 doigt, 2 doigts, ... 10
doigts) - doigt ou "pen"
Lorsque cet événement est appelé, nous devons - déclare un tableau d'informations toucher
- allouer un tableau pour récupérer les informations de chaque doigt
- transférer les informations dans notre tableau par GetTouchInputInfo
- lire les information pour chaque doigt
Var l_touchinput_array: Array Of TTouchInput;
l_touchinput: TTouchInput;
SetLength(l_touchinput_array, Message.WParam);
GetTouchInputInfo(Message.LParam, Message.WParam,
@l_touchinput_array[0], SizeOf(TTouchInput));
For l_touchinput In l_touchinput_array Do
... | tTouchInput est défini dans Winapi.Windows par
Type tTouchInput = Record
x: Integer;
y: Integer;
hSource: THandle;
dwID: DWORD;
dwFlags: DWORD;
dwMask: DWORD;
dwTime: DWORD;
dwExtraInfo: ULONG_PTR;
cxContact: DWORD;
cyContact: DWORD; End;
| et - x et y sont les coordonnées du point de toucher en "centièmes de pixel physique d'écran" (pour accomoder la haute définition)
- hSource: la poignée du périphérique de saisie
- dwID: un identificateur dont la valeur restera la même pendant toute la durée du toucher
- dwFlags, dwMask: des informations sur le type de toucher (presser, relever, déplacement etc)
- dwTime: la date de cet événement
- dwExtraInfo
- cxContact, cyContact: la taille de la zone de contact
Il faudra en général convertir les "centièmes de pixel physique d'écran" en
pixel logiques en appelant PhysicalToLogicalPoint
Function PhysicalToLogicalPoint(hWnd: HWND; Var lpPoint: TPoint): BOOL; Stdcall
| et - handle est la poignée du contrôle windows (la fenêtre ou un autre tWinControl)
- lpPoint est notre point physique en entrée, et point logique en sortie (paramètre Var)
Comme de plus les coordonnées sont relatives à l'écran, nous appelons aussi, en général ScreenToClient pour avoir des coordonnées relatives à notre fenêtre
Voici une application qui affiche le contenu de wm_touch
| créez une application VCL | |
créez les événements qui enregistrent et dé-enregistrent la réception des événements wm_Touch
Type //
TForm1 = Class(TForm) // ...
Private
Procedure CreateWnd; Override;
Procedure DestroyWnd; Override;
Public End; Var
Form1: TForm1; Implementation
Uses Vcl.Touch.Gestures; {$R *.dfm}
Procedure TForm1.CreateWnd; Begin
Inherited; RegisterTouchWindow(Handle, 0);
End; // CreateWnd
Procedure TForm1.DestroyWnd; Begin
Inherited; UnregisterTouchWindow(Handle);
End; // DestroyWnd End | |
| créez l'événement OnMouseMove pour vérifier les informations wm_touch
Type //
TForm1 = Class(TForm)
Procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
// ... End;
Var g_mousemove_X, g_mousemove_Y: Integer;
Procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer); Begin
Panel1.Caption:= Format(' X=%3d Y=%3d', [X, Y]);
g_mousemove_X:= X;
g_mousemove_Y:= Y;
End; // FormMouseMove End | |
| créez un gestionnaire des messages wm_Touch qui affiche la position de chaque doigt :
Type TForm1 = Class(TForm)
Public
Procedure handle_wm_touch(Var Message: TMessage); message WM_TOUCH;
End;
Procedure display(p_text: String);
Begin
Form1.Memo1.Lines.Add(p_text);
End;
Procedure TForm1.handle_wm_touch(Var Message: TMessage);
Function f_touchpoint_to_logical_point(Const p_touch_point: TTouchInput): TPoint;
// -- converts the physical (hi def) coordinates into physical screen pixels
Begin
Result := Point(p_touch_point.X Div 100, p_touch_point.Y Div 100);
PhysicalToLogicalPoint(Handle, Result);
End; // f_touchpoint_to_logical_point
Var l_touchinput_array: Array Of TTouchInput;
l_touchinput: TTouchInput;
l_handled: Boolean;
l_logical_point: TPoint;
l_screen_point: tPoint;
Begin // handle_wm_touch
l_handled := False;
display('wm_touch, count '+ IntToStr(Message.WParam));
SetLength(l_touchinput_array, Message.WParam);
GetTouchInputInfo(Message.LParam, Message.WParam, @l_touchinput_array[0],
SizeOf(TTouchInput)); Try
For l_touchinput In l_touchinput_array Do
Begin
l_logical_point:= f_touchpoint_to_logical_point(l_touchinput);
l_screen_point:= ScreenToClient(l_logical_point);
display(Format (' scr X=%3d Y=%3d screen_X=%3d Y=%3d move_X=%3d Y=%3d %s',
[l_logical_point.X, l_logical_point.Y,
l_screen_point.X, l_screen_point.Y,
g_mousemove_X, g_mousemove_Y, '<']));
End; // for l_touchinput
l_handled := True; Finally
If l_handled Then
CloseTouchInputHandle(Message.LParam)
Else Inherited;
End; End; // handle_wm_touch |
| | compilez et exécutez | |
voici un exemple d'affichage avec 1 doigt : | |
voici un exemple d'affichage avec 2 doigts : |
Nous pouvons exploiter wm_Touch pour déplacer un élément à l'écran. Nous
pouvons, par exemple, déplacer un cercle à l'écran :
\Var g_center_x, g_center_y: Integer;
Procedure TForm1.FormPaint(Sender: TObject);
Begin
Canvas.Ellipse(g_center_x- k_radius, g_center_y- k_radius,
g_center_x+ k_radius, g_center_y+ k_radius);
End;
Procedure TForm1.handle_wm_touch(Var Message: TMessage);
// ooo Begin // handle_wm_touch // ooo
Try
For l_touchinput In l_touchinput_array Do
Begin
l_logical_point:= f_touchpoint_to_logical_point(l_touchinput);
l_screen_point:= ScreenToClient(l_logical_point);
If (l_touchinput.dwFlags And TouchEventF_Move)<> 0
Then Begin
g_center_x:= l_screen_point.X;
g_center_y:= l_screen_point.Y;
OnPaint(Nil);
End;
End; // for l_touchinput // ooo
End; // handle_wm_touch | et voici un exemple d'exécution : Notez que : - nous avons volontairement appelé OnPaint pour afficher un nouveau cercle, ce qui laisse les cercles précédents à l'écran. Pour ne visualiser que la
position courante, il faut appeler wla nouvelle
- nous pouvons aussi utiliser les autres constantes telles que TouchEventF_Up etc pour traiter les autres types d'actions
- pour déplacer le cercle uniquement si le doigt est dans le cercle, il faudrait tester la position du doigt par rapport au cercle
- comme nous pouvons détecter 2 doigts, nous pourrions aussi tester l'action
des deux doigts (agrandir le cercle etc)
4.2 - Toucher et Inertie Windows 7 propose aussi un "Manipulation Engine" et "Inertia Engine" qui permettent des effets tels que des continuation ou des amortissements de mouvements.
Ces moteurs peuvent être utilisés sans le toucher, mais dans l'exemple qui suit, nous allons utiliser le toucher d'un point pour déplacer un cercle à l'écran. Pour utiliser ces moteurs:
- nous créons une Classe Delphi descendante de _IManipulationEvents
- cette Classe crée les deux moteurs, sous forme d'objets COM
- il faut ensuite implémenter les méthodes exigée par _IManipulationEvents.
Au niveau fonctionnement: - l'objet COM manipulateur
- est informé des événements souris (toucher) par les méthodes ProcessDown, ProcessMove, ProcessUp
- peut fournir des information par des méthodes telles que GetVelocityX
- l'objet COM inertie
- est initialisé par des limites de déplacement (put_BoundaryLeft ...)
- peut utiliser des lignes d'élasticité (rebondissement) (put_ElasticMarginLeft ...)
- l'inertie est lancée par put_InitialVelocityX, et put_InitialOriginX
- peut fournir des informations en interrogeant Process
A titre d'exemple - nous déplaçons avec inertie un cercle à l'écran
- nous définissons deux rectangles à l'écran:
- un rectangle intérieur où le cercle ne doit pas pénétrer'
- un rectangle englobant le précédent sur les paroi duquel le cercle rebondira
- nous trainerons le doigt à l'écran, ce qui lancera le mouvement, et, dès que
nous lèverons le doit, le cercle continuera sur sa lancée, en rebondissant sur la paroi elastique, avec un certain ralentissement
Au niveau Delphi - le cercle, avec ses moteurs COM sera initialisé dans OnCreate
- les déplacement souris appelleront les mises à jour du manipulateur (down, move, up), qui afficheront le cercle
- lorsque nous relèverons le doigt, le moteur d'inertie sera lancé. Pour
visualiser le déplacement du cercle, nous utiliserons un tTimer qui affichera la position du cercle durant cette fin de trajectoire
Comme pour le déplacement précédent, nous conserverons les différents affichages à l'écran :
- le premier toucher réinitialise l'affichage
- lors du déplacement souris, le cercle sera vert
- lors de mouvement piloté par l'inertie, le cercle sera jaune
- la position finale sera rouge
Voici le code commenté
- la Classe est déclarée par:
Uses Classes, Manipulations, SysUtils, Graphics, Forms;
Type c_bouncing_ball =
Class(TInterfacedObject, _IManipulationEvents)
Private
FInertia: IInertiaProcessor;
FManipulator: IManipulationProcessor;
FInertiaCookie, FManipulatorCookie: LongInt;
m_iniertia_completed: LongBool;
m_c_canvas: tCanvas;
m_mouse_is_down: Boolean;
m_boundary_width, m_boundary_height: Integer;
Public
m_x, m_y, m_radius: Integer;
// -- IS is required by manipulator
m_id: Integer;
m_with_inertia, m_with_manipulator: Boolean;
{ _IManipulationEvents }
Function ManipulationStarted(p_x: Single; p_y: Single): HRESULT; Stdcall;
Function ManipulationDelta(p_x: Single; p_y: Single;
translationDeltaX: Single;
translationDeltaY: Single; scaleDelta: Single; expansionDelta: Single;
rotationDelta: Single; cumulativeTranslationX: Single;
cumulativeTranslationY: Single; cumulativeScale: Single;
cumulativeExpansion: Single; cumulativeRotation: Single): HRESULT;
Stdcall;
Function ManipulationCompleted(p_x: Single; p_y: Single;
cumulativeTranslationX: Single; cumulativeTranslationY: Single;
cumulativeScale: Single; cumulativeExpansion: Single;
cumulativeRotation: Single): HRESULT; Stdcall;
Public
Constructor create_bouncing_ball(p_width, p_height: Integer;
p_inertia, p_manipulator: Boolean;
p_c_canvas: tCanvas);
Procedure draw_ball(p_color: Integer);
Procedure do_mouse_down(p_x, p_y: Integer);
Procedure do_mouse_move(p_x, p_y: Integer);
Procedure do_mouse_up(p_x, p_y: Integer);
Procedure disconnect_engines;
Procedure process_inertia;
Destructor Destroy; Override;
End; // c_bouncing_ball |
- le Constructor initialise les objets COM et les paramètres des parois
Constructor c_bouncing_ball.create_bouncing_ball(p_width, p_height: Integer;
p_inertia, p_manipulator: Boolean; p_c_canvas: tCanvas);
Begin Inherited Create;
m_boundary_width:= p_width; m_boundary_height:= p_height;
m_with_inertia:= p_inertia; m_with_manipulator:= p_manipulator;
m_c_canvas:= p_c_canvas;
m_x := p_width Div 2;
m_y := p_height Div 2; m_radius := 20;
m_id := 1; m_iniertia_completed := True;
FInertia := CreateComObject(CLSID_IInertiaProcessor) As IInertiaProcessor;
FManipulator := CreateComObject(CLSID_IManipulationProcessor)
As IManipulationProcessor;
InterfaceConnect(FInertia, _IManipulationEvents, Self, FInertiaCookie);
InterfaceConnect(FManipulator, _IManipulationEvents, Self, FManipulatorCookie);
FInertia.put_DesiredDeceleration(0.001); FInertia.put_BoundaryLeft(200);
FInertia.put_BoundaryTop(200);
FInertia.put_BoundaryRight(m_boundary_width- 100);
FInertia.put_BoundaryBottom(m_boundary_height- 100);
FInertia.put_ElasticMarginLeft(100); FInertia.put_ElasticMarginTop(100);
FInertia.put_ElasticMarginRight(100); FInertia.put_ElasticMarginBottom(100);
End; // create_bouncing_ball | - les événements exigés par _IManipulationEvents sont
Function c_bouncing_ball.ManipulationStarted(p_x, p_y: Single): HRESULT;
Begin Result := S_OK; End;
Function c_bouncing_ball.ManipulationDelta(p_x, p_y, translationDeltaX, translationDeltaY,
scaleDelta, expansionDelta, rotationDelta, cumulativeTranslationX,
cumulativeTranslationY, cumulativeScale, cumulativeExpansion,
cumulativeRotation: Single): HRESULT; Begin
m_x:= Round(p_x);
m_y := Round(p_y); Result:= S_OK;
End;
Function c_bouncing_ball.ManipulationCompleted(p_x, p_y, cumulativeTranslationX,
cumulativeTranslationY, cumulativeScale, cumulativeExpansion,
cumulativeRotation: Single): HRESULT; Begin
Result := S_OK; End; |
ManipulationDelta mettra à jour la position du cercle à partir des information du manipulateur - nos déplacements souris appelleront
Procedure c_bouncing_ball.do_mouse_down(p_x, p_y: Integer);
Begin
FManipulator.ProcessDown(m_id, p_x, p_y);
draw_ball(clBlue); m_mouse_is_down:= True;
End; // do_mouse_down
Procedure c_bouncing_ball.do_mouse_move(p_x, p_y: Integer);
Begin If m_mouse_is_down
Then Begin
FManipulator.ProcessMove(m_id, p_x, p_y);
draw_ball(clLime); End;
End; // do_mouse_move
Procedure c_bouncing_ball.do_mouse_up(p_x, p_y: Integer);
Var l_vx, l_vy: Single; Begin
FManipulator.ProcessUp(m_id, p_x, p_y);
draw_ball(clGreen); If m_with_inertia
Then Begin
FManipulator.GetVelocityX(l_Vx);
FManipulator.GetVelocityY(l_Vy);
FInertia.put_InitialVelocityX(l_Vx);
FInertia.put_InitialVelocityY(l_Vy);
FInertia.put_InitialOriginX(m_x);
FInertia.put_InitialOriginY(m_y);
End; m_iniertia_completed := False;
m_mouse_is_down:= False; End; // do_mouse_up |
Ici, c'est ProcessMove qui renseigne le moteur sur la position de notre doigt. Puis ManipulationDelta mettra à jour la position de notre cercle - le traitement inertiel est le suivant :
Procedure c_bouncing_ball.process_inertia;
Var l_x_velocity: single; Begin
If m_with_inertia Then Begin
If m_iniertia_completed
Then draw_ball(clRed)
Else Begin
fInertia.Process(m_iniertia_completed);
draw_ball(clYellow);
End; End;
FManipulator.GetVelocityX (l_x_velocity);
If Not m_iniertia_completed
Then display('vel_x '+ FloatToStr(l_x_velocity));
End; // process_inertia | - et l'affichage du cercle est simplement
Procedure c_bouncing_ball.draw_ball(p_color: Integer);
Begin m_c_canvas.Brush.Color := p_color;
m_c_canvas.Ellipse(m_x- m_radius, m_y- m_radius, m_x+ m_radius, m_y+ m_radius);
DrawFocusRect(m_c_canvas.Handle,
Rect (100, 100, m_boundary_width, m_boundary_height) );
DrawFocusRect(m_c_canvas.Handle,
Rect (200, 200, m_boundary_width- 100, m_boundary_height- 100));
End; | A présent, voici la forme: - la création de la forme et du cercle :
Var g_area_width: Integer= 0;
g_c_bouncing_ball: c_bouncing_ball= Nil;
Procedure TForm1.FormCreate(Sender: TObject);
Begin initialize_display(Memo1.Lines);
// -- disable div by 0 exceptions for the inertia processor
Set8087CW($133F);
g_area_width:= Width - Memo1.Width;
g_c_bouncing_ball := c_bouncing_ball.create_bouncing_ball(
g_area_width- 100, Height- 100,
inertia_.Checked, manipulator_.Checked, Canvas);
End; // FormCreate | - les événements souris (provoqués par la souris, ou, dans notre cas, le doigt):
Procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
Begin display('> FormMouseDown');
Timer1.Enabled := False;
g_c_bouncing_ball.do_mouse_down(X, Y);
// -- clear the scree before each move Invalidate;
End; // FormMouseDown
Procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer); Begin
display(' FormMouseMove');
g_c_bouncing_ball.do_mouse_move(X, Y);
If invalidate_.Checked
Then Invalidate; End; // FormMouseMove
Procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
Begin display('< FormMouseUP');
g_c_bouncing_ball.do_mouse_up(X, Y);
If invalidate_.Checked
Then Invalidate;
Timer1.Enabled := True;
End; // FormMouseUp | - et le traitement du timer qui affiche la balle en jaune et interroge la vélocité inertielle :
Procedure TForm1.Timer1Timer(Sender: TObject);
Begin g_c_bouncing_ball.process_inertia;
If invalidate_.Checked
Then Invalidate End; // Timer1Timer |
Voici un exemple ou nous faisons un arc en accélérant de la droite vers la gauche (vert), et l'inertie qui poursuit le mouvement en ralentissant (jaune) :
Notez que - plus les points sont rapprochés, plus le mouvement est lent
5 - Améliorations et Remarques Il reste d'autres points à examiner - la gestion de Direct2d et les WIC images. Ces possibilités permettent les
manipulations d'images à l'écran (déplacements, agrandissements etc)
- la gestion du clavier à l'écran
Mentionnons aussi que nous avions commencé à essayer nos programmes de gestes
en Delphi XE2, puis avons basculé en XE3 pour le multi-touch. Certaines adaptations ont été nécessaires dans les primitives de conversion wm_Touch. Il est possible que ces librairies subissent d'autres changements pour leur
adaptation / intégration à FireMonkey, iOs, Android etc.
Ces techniques s'avèreront fondamentales pour la gestion des téléphones et des tablettes.
6 - Télécharger le code source Delphi Vous pouvez télécharger:
Comme d'habitude:
- nous vous remercions de nous signaler toute erreur, inexactitude ou problème de téléchargement en envoyant un e-mail à jcolibri@jcolibri.com. Les corrections
qui en résulteront pourront aider les prochains lecteurs
- tous vos commentaires, remarques, questions, critiques, suggestion d'article, ou mentions d'autres sources sur le même sujet seront de même
les bienvenus à jcolibri@jcolibri.com.
- plus simplement, vous pouvez taper (anonymement ou en fournissant votre e-mail pour une réponse) vos commentaires ci-dessus et nous les envoyer en
cliquant "envoyer" :
- et si vous avez apprécié cet article, faites connaître notre site,
ajoutez un lien dans vos listes de liens ou citez-nous dans vos blogs ou réponses sur les messageries. C'est très simple: plus nous aurons de visiteurs et de références Google, plus nous écrirons d'articles.
7 - Références Les codes sources ont été inspirés par les articles suivants;
Mentionnons aussi que nous avions commencé par tester les gestes simples - Gestes Delphi
John COLIBRI - Déc 2012 - (== "Delphi Gestures" =="mouvements delphi") : gestion Delphi des mouvements effectués par un utilisateur avec le doigt sur un éran tactile
8 - L'auteur
John COLIBRI est passionné par le développement Delphi et les applications de Bases de Données. Il a écrit de nombreux livres et articles, et partage son temps entre le développement de projets (nouveaux projets, maintenance, audit, migration BDE, migration Xe_n, refactoring) pour ses clients, le
conseil (composants, architecture, test) et la
formation. Son site contient des articles
avec code source, ainsi que le programme et le calendrier des stages de formation Delphi, base de données, programmation objet, Services Web, Tcp/Ip et
UML qu'il anime personellement tous les mois, à Paris, en province ou sur site client. |